package com.huawei.app.demo.video;

import java.nio.ByteBuffer;
import java.util.concurrent.ArrayBlockingQueue;

import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.os.Message;
import android.view.Surface;
import android.view.View;

import com.huawei.app.demo.MsgHandler;
import com.huawei.app.demo.R;
import com.huawei.app.demo.util.LogUtils;
import com.huawei.app.httpapi.ThreadPools;

/**
 * H264 解码任务
 *
 * @since 2020-08-14
 */
public class AvcDecoderTask implements Runnable {

    private static final String TAG = AvcDecoderTask.class.getSimpleName();

    public boolean isRuning = true;
    private MediaCodec mediaCodecDecoder;
    private Surface surface;
    private ArrayBlockingQueue<ImgBuffer> receiverQueue;

    private int width;
    private int height;
    private int framerate;
    private int bitrate = 12000000;

    private boolean isFirst = true;
    private MsgHandler msgHandler;

    public AvcDecoderTask(int width, int height, int framerate, ArrayBlockingQueue<ImgBuffer> receiverQueue) {
        this.width = width;
        this.height = height;
        this.framerate = framerate;
        this.receiverQueue = receiverQueue;
    }

    public void setOutputSurface(Surface outputSurface) {
        this.surface = outputSurface;
    }

    /**
     * 初始化解码器
     */
    public void initDecoder() {
        try {
            // 初始化解码器
            mediaCodecDecoder = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
            // mediaCodecDecoder = MediaCodec.createByCodecName("OMX.google.h264.decoder");
            // mediaCodecDecoder = MediaCodec.createByCodecName("OMX.hisi.video.decoder.avc");
            MediaFormat mediaFormatDecoder = createMediaFormat(width, height);
            mediaCodecDecoder.configure(mediaFormatDecoder, surface, null, 0);
            mediaCodecDecoder.start();
            LogUtils.getInstance().i(TAG, "init decoder success.");
        } catch (Exception e) {
            LogUtils.getInstance().e(TAG, "init decoder failed.", e);
        }
    }

    /**
     * 重置解码器
     */
    private void resetDecoder() {
        if (mediaCodecDecoder == null) {
            initDecoder();
            return;
        }

        mediaCodecDecoder.stop();
        MediaFormat mediaFormatDecoder = createMediaFormat(this.width, this.height);
        mediaCodecDecoder.configure(mediaFormatDecoder, surface, null, 0);
        mediaCodecDecoder.start();
    }

    private MediaFormat createMediaFormat(int width, int height) {
        MediaFormat mediaFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, width, height);
        mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible); // 颜色格式

        // 码率模式
        // CQ 不控制码率, 尽最大可能保证图像质量
        // CBR 静态码率
        // VBR 动态码率, 根据图像内容的复杂度来动态调整输出码率, 图像复杂则码率高, 图像简单则码率低
        mediaFormat.setInteger(MediaFormat.KEY_BITRATE_MODE, MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBR);
        mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate); // 码率
        mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, framerate); // 帧率
        mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1); // 帧间间隔, 单位秒
        mediaFormat.setInteger(MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline); // H264 BP
        mediaFormat.setInteger(MediaFormat.KEY_LEVEL, MediaCodecInfo.CodecProfileLevel.AVCLevel41);

        LogUtils.getInstance().i(TAG, "createMediaFormat "
                + " profile: " + mediaFormat.getInteger(MediaFormat.KEY_PROFILE) + ", "
                + " level: " + mediaFormat.getInteger(MediaFormat.KEY_LEVEL));
        return mediaFormat;
    }

    @Override
    public void run() {
        String oldThreadName = Thread.currentThread().getName();
        Thread.currentThread().setName(this.getClass().getSimpleName());

        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
        while (this.isRuning) {
            ImgBuffer imgBuffer = this.receiverQueue.poll();
            if (imgBuffer == null) {
                ThreadPools.pause(10);
                continue;
            }

            if (isFirst) {
                // 保证从第一帧开始, 防止序号错乱导致编码后出现黑屏
                if (imgBuffer.getSnoIndex() > 1) {
                    continue;
                }
                hideTip();
                isFirst = false;
                adjustResolution(imgBuffer.getWidth(), imgBuffer.getHeight());
            }

            byte[] data = imgBuffer.getData();
            long presentationTimeUs = computePresentationTime(imgBuffer.getSno());

            // 开始解码
            long beginTime = System.currentTimeMillis();
            int inputBufferIndex = mediaCodecDecoder.dequeueInputBuffer(-1);
            if (inputBufferIndex >= 0) {
                ByteBuffer inputBuffer = mediaCodecDecoder.getInputBuffer(inputBufferIndex);
                inputBuffer.clear();
                inputBuffer.put(data);
                mediaCodecDecoder.queueInputBuffer(inputBufferIndex, 0, data.length, presentationTimeUs, 0);
            }

            int outputBufferIndex = mediaCodecDecoder.dequeueOutputBuffer(bufferInfo,0);
            while (outputBufferIndex >= 0) {
                mediaCodecDecoder.releaseOutputBuffer(outputBufferIndex, true);
                outputBufferIndex = mediaCodecDecoder.dequeueOutputBuffer(bufferInfo, 0);
            }

            // 统计解码时间
            long endTime = System.currentTimeMillis();
            long time = endTime - beginTime;
            LogUtils.getInstance().v(TAG, "decode time: " + time
                    + ", receiverQueue: " + receiverQueue.size()
                    + ", sno: " + imgBuffer.getSno()
                    + ", imgBuffer: " + imgBuffer);
        }

        Thread.currentThread().setName(oldThreadName);
    }

    /**
     * Generates the presentation time for frame N, in microseconds.
     */
    private long computePresentationTime(long frameIndex) {
        // 1秒 = 1000毫秒(milliseconds) = 1000000微秒(microseconds)
        // 1000000 / m_framerate 表示帧间间隔
        return 132 + frameIndex * 1000000 / framerate;
    }

    /**
     * 调整解码器的分辨率
     * @param width
     * @param height
     */
    private void adjustResolution(int width, int height) {
        if (this.width == width && this.height == height) {
            return;
        }

        LogUtils.getInstance().i(TAG, "adjustResolution begin " + this.width + "x" + this.height);
        this.width = width;
        this.height = height;
        resetDecoder();
        LogUtils.getInstance().i(TAG, "adjustResolution end " + this.width + "x" + this.height);
    }

    public void stop() {
        if (!isRuning) {
            return;
        }

        try {
            isRuning = false;
            if (mediaCodecDecoder != null) {
                mediaCodecDecoder.stop();
                mediaCodecDecoder.release();
            }
        } catch (Exception e){
            LogUtils.getInstance().e(TAG, e.getMessage());
        } finally {
            mediaCodecDecoder = null;
        }
    }

    public MsgHandler getMsgHandler() {
        return msgHandler;
    }

    public void setMsgHandler(MsgHandler msgHandler) {
        this.msgHandler = msgHandler;
    }

    private void hideTip() {
        if (msgHandler == null) {
            return;
        }

        Message msg = Message.obtain();
        msg.what = MsgHandler.UPDATE_VIEW_SHOW_OR_HIDE;
        msg.arg1 = R.id.shareScreenRecvTip;
        msg.obj = View.GONE;
        msgHandler.sendMessage(msg);
    }
}